package apple.core;

import java.io.IOException;

import org.apache.log4j.Logger;

public class DiskII {

   static Logger logger = Logger.getLogger(DiskII.class);

   private static final int MAX_TRACK_NUMBER = 69;

   /*
    * Begin soft toggles
    */

   private boolean[] phases = { true, false, false, false };

   private boolean motor = false;

   private boolean drive = false;

   private boolean q6 = false;

   private boolean q7 = false;

   /*
    * End soft toggles
    */

   private int lastPhase = 0;

   byte[][] diskTracks;

   /*
    * These are "half-tracks". Assume that disk arm starts halfway into the
    * disk.
    */
   private int currentTrack = 40;

   private int currentTrackIndex = 0;

   private static final byte PROM[] = { (byte) 0xA2, (byte) 0x20, (byte) 0xA0,
         (byte) 0x00, (byte) 0xA2, (byte) 0x03, (byte) 0x86, (byte) 0x3C,
         (byte) 0x8A, (byte) 0x0A, (byte) 0x24, (byte) 0x3C, (byte) 0xF0,
         (byte) 0x10, (byte) 0x05, (byte) 0x3C, (byte) 0x49, (byte) 0xFF,
         (byte) 0x29, (byte) 0x7E, (byte) 0xB0, (byte) 0x08, (byte) 0x4A,
         (byte) 0xD0, (byte) 0xFB, (byte) 0x98, (byte) 0x9D, (byte) 0x56,
         (byte) 0x03, (byte) 0xC8, (byte) 0xE8, (byte) 0x10, (byte) 0xE5,
         (byte) 0x20, (byte) 0x58, (byte) 0xFF, (byte) 0xBA, (byte) 0xBD,
         (byte) 0x00, (byte) 0x01, (byte) 0x0A, (byte) 0x0A, (byte) 0x0A,
         (byte) 0x0A, (byte) 0x85, (byte) 0x2B, (byte) 0xAA, (byte) 0xBD,
         (byte) 0x8E, (byte) 0xC0, (byte) 0xBD, (byte) 0x8C, (byte) 0xC0,
         (byte) 0xBD, (byte) 0x8A, (byte) 0xC0, (byte) 0xBD, (byte) 0x89,
         (byte) 0xC0, (byte) 0xA0, (byte) 0x50, (byte) 0xBD, (byte) 0x80,
         (byte) 0xC0, (byte) 0x98, (byte) 0x29, (byte) 0x03, (byte) 0x0A,
         (byte) 0x05, (byte) 0x2B, (byte) 0xAA, (byte) 0xBD, (byte) 0x81,
         (byte) 0xC0, (byte) 0xA9, (byte) 0x56, (byte) 0x20, (byte) 0xA8,
         (byte) 0xFC, (byte) 0x88, (byte) 0x10, (byte) 0xEB, (byte) 0x85,
         (byte) 0x26, (byte) 0x85, (byte) 0x3D, (byte) 0x85, (byte) 0x41,
         (byte) 0xA9, (byte) 0x08, (byte) 0x85, (byte) 0x27, (byte) 0x18,
         (byte) 0x08, (byte) 0xBD, (byte) 0x8C, (byte) 0xC0, (byte) 0x10,
         (byte) 0xFB, (byte) 0x49, (byte) 0xD5, (byte) 0xD0, (byte) 0xF7,
         (byte) 0xBD, (byte) 0x8C, (byte) 0xC0, (byte) 0x10, (byte) 0xFB,
         (byte) 0xC9, (byte) 0xAA, (byte) 0xD0, (byte) 0xF3, (byte) 0xEA,
         (byte) 0xBD, (byte) 0x8C, (byte) 0xC0, (byte) 0x10, (byte) 0xFB,
         (byte) 0xC9, (byte) 0x96, (byte) 0xF0, (byte) 0x09, (byte) 0x28,
         (byte) 0x90, (byte) 0xDF, (byte) 0x49, (byte) 0xAD, (byte) 0xF0,
         (byte) 0x25, (byte) 0xD0, (byte) 0xD9, (byte) 0xA0, (byte) 0x03,
         (byte) 0x85, (byte) 0x40, (byte) 0xBD, (byte) 0x8C, (byte) 0xC0,
         (byte) 0x10, (byte) 0xFB, (byte) 0x2A, (byte) 0x85, (byte) 0x3C,
         (byte) 0xBD, (byte) 0x8C, (byte) 0xC0, (byte) 0x10, (byte) 0xFB,
         (byte) 0x25, (byte) 0x3C, (byte) 0x88, (byte) 0xD0, (byte) 0xEC,
         (byte) 0x28, (byte) 0xC5, (byte) 0x3D, (byte) 0xD0, (byte) 0xBE,
         (byte) 0xA5, (byte) 0x40, (byte) 0xC5, (byte) 0x41, (byte) 0xD0,
         (byte) 0xB8, (byte) 0xB0, (byte) 0xB7, (byte) 0xA0, (byte) 0x56,
         (byte) 0x84, (byte) 0x3C, (byte) 0xBC, (byte) 0x8C, (byte) 0xC0,
         (byte) 0x10, (byte) 0xFB, (byte) 0x59, (byte) 0xD6, (byte) 0x02,
         (byte) 0xA4, (byte) 0x3C, (byte) 0x88, (byte) 0x99, (byte) 0x00,
         (byte) 0x03, (byte) 0xD0, (byte) 0xEE, (byte) 0x84, (byte) 0x3C,
         (byte) 0xBC, (byte) 0x8C, (byte) 0xC0, (byte) 0x10, (byte) 0xFB,
         (byte) 0x59, (byte) 0xD6, (byte) 0x02, (byte) 0xA4, (byte) 0x3C,
         (byte) 0x91, (byte) 0x26, (byte) 0xC8, (byte) 0xD0, (byte) 0xEF,
         (byte) 0xBC, (byte) 0x8C, (byte) 0xC0, (byte) 0x10, (byte) 0xFB,
         (byte) 0x59, (byte) 0xD6, (byte) 0x02, (byte) 0xD0, (byte) 0x87,
         (byte) 0xA0, (byte) 0x00, (byte) 0xA2, (byte) 0x56, (byte) 0xCA,
         (byte) 0x30, (byte) 0xFB, (byte) 0xB1, (byte) 0x26, (byte) 0x5E,
         (byte) 0x00, (byte) 0x03, (byte) 0x2A, (byte) 0x5E, (byte) 0x00,
         (byte) 0x03, (byte) 0x2A, (byte) 0x91, (byte) 0x26, (byte) 0xC8,
         (byte) 0xD0, (byte) 0xEE, (byte) 0xE6, (byte) 0x27, (byte) 0xE6,
         (byte) 0x3D, (byte) 0xA5, (byte) 0x3D, (byte) 0xCD, (byte) 0x00,
         (byte) 0x08, (byte) 0xA6, (byte) 0x2B, (byte) 0x90, (byte) 0xDB,
         (byte) 0x4C, (byte) 0x01, (byte) 0x08, (byte) 0x00, (byte) 0x00,
         (byte) 0x00, (byte) 0x00, (byte) 0x00 };

   public static byte readProm(byte address) {
      // convert to unsigned for array access
      int unsignedAddress = address & 0xFF;
      byte datum = PROM[unsignedAddress];
      return datum;
   }

   public byte toggle(byte address) {
      int phase;
      switch (address) {
      case 0x0:
      case 0x2:
      case 0x4:
      case 0x6:
         phase = address / 2;
         phases[phase] = false;
         logger.debug(String.format("Phase %d off", phase));
         break;
      case 0x1:
      case 0x3:
      case 0x5:
      case 0x7:
         phase = (address - 1) / 2;
         phases[phase] = true;
         logger.debug(String.format("Phase %d on", phase));
         if (phase == (lastPhase + 1) % 4) {
            if (currentTrack < MAX_TRACK_NUMBER) {
               currentTrack++;
            }
            logger.debug(String.format("Moved in to track %d", currentTrack));
         }
         if (phase == (lastPhase + 3) % 4) {
            if (currentTrack > 0) {
               currentTrack--;
            }
            logger
                  .debug(String.format("Moved out to track %d", currentTrack));
         }
         lastPhase = phase;
         break;
      case 0x8:
         motor = false;
         logger.debug("Motor off");
         break;
      case 0x9:
         motor = true;
         logger.debug("Motor on");
         break;
      case 0xA:
         drive = false;
         logger.debug("Drive 1");
         break;
      case 0xB:
         drive = true;
         logger.debug("Drive 2");
         break;
      case 0xC:
         q6 = false;
         logger.trace("Q6 low");
         if (!q7) {
            int dosTrack = currentTrack / 2;
            byte datum = diskTracks[dosTrack][currentTrackIndex];
            currentTrackIndex = (currentTrackIndex + 1)
                  % diskTracks[dosTrack].length;
            logger.trace(String.format("Read: %02X", datum));
            return datum;
         }
         break;
      case 0xD:
         q6 = true;
         logger.debug("Q6 high");
         break;
      case 0xE:
         q7 = false;
         logger.debug("Q7 low");
         break;
      case 0xF:
         q7 = true;
         logger.debug("Q7 high");
         break;
      default:
         assert false;
         break;
      }
      return 0x00;
   }

   public void insertDisk(String filename) throws IOException {
      byte[][] diskImage = DiskImage.loadImage(filename);
      byte[][] nibblizedTracks = DiskImage.nibblizeImage(diskImage);
      this.diskTracks = nibblizedTracks;
   }
}
